package opmq

import (
	"fmt"
	"gitee.com/maomaomaoge/opmq/packets"
	"strings"
	"sync"
)

// UnorderedTree topic和连接的内存关系布局
// 适用于通配符
// 注意指针位置
// 内存布局:  连接hz有两个topic: /dev/41/42/# 和 /dev2/41/42/#
//                  ""(空字符串的根节点)
//                /    \
//              dev1  dev2
//              /      /
//            41      41
//            /       /
//           #       #
//       (连接hz)   (连接hz)
//
// Note: 这个不是简单的二叉树，只是普通的无序数
type UnorderedTree struct {
	Mux sync.Mutex

	// 当前节点的名字
	Name string

	// 树的孩子节点
	Children []*UnorderedTree

	// 当前树节点的所有连接
	conns []*clientConn
}

func NewTree() *UnorderedTree {
	return &UnorderedTree{
		Children: make([]*UnorderedTree, 0),
		conns:    make([]*clientConn, 0),
	}
}

// AddChildAndReturnNext 添加孩子节点，并且返回孩子节点的位置
// - 目的是不改变root的指针位置，只是获取临时的孩子节点的指针位置
func (ut *UnorderedTree) AddChildAndReturnNext(name string) *UnorderedTree {
	tree := NewTree()
	tree.Name = name

	// 如果有重复节点，直接返回
	for _, v := range ut.Children {
		if v.Name == name {
			return v
		}
	}

	ut.Children = append(ut.Children, tree)
	return tree
}

// Find 查询当前节点的孩子节点存不存在
// 创建节点时候使用
func (ut *UnorderedTree) Find(name string) (*UnorderedTree, bool) {
	DEBUG_TREE.Println(ut.Name, "孩子个数", len(ut.Children))
	for _, v := range ut.Children {
		if v.Name == name || v.Name == "+" || v.Name == "#" {
			return v, true
		}
	}

	return nil, false
}

// AddTopic 添加topic
// - 找到孩子节点，看看存在与否，没有就添加，有就指针到孩子节点的连接，
func AddTopic(topic string, conn *clientConn, ut *UnorderedTree) {
	split := strings.SplitN(topic, "/", 2)

	// topic没有匹配完全，继续匹配
	//  假如原始的topic是 /dev/41/42/#,此时字符串是两部分: 41, 42/#。
	//  在41节点下面找节点名字为42的.
	//  没有找到就去创建，以此类推.
	if len(split) == 2 {
		ut = ut.AddChildAndReturnNext(split[0])
		DEBUG_TREE.Println("完整的", split, "还剩下: ", split[1])
		AddTopic(split[1], conn, ut)
	}

	// 此时topic只剩下 # 还没匹配
	// ⚠️ WARNING:
	//  - 此时不是最后一个节点 # ，是最后一个节点的父亲42，让42添加孩子节点 # ，孩子节点 # 添加连接属性
	//  - 此时还可能是掉线的连接(！掉线连接可能会发送多次)。首先去看连接属性的发送掉线数据标志位，然后判断是否进行发送掉线retain数据
	if len(split) == 1 { // 代表topic的最后一个节点， 此时前面没有节点创建，会出现通配符，进行通配符添加规则
		child, ok := ut.Find(split[0])
		if !ok {
			DEBUG_TREE.Println("ut: ", ut.Name, " s0: ", split[0])
			lastNode := &UnorderedTree{
				Name:     split[0],
				Children: make([]*UnorderedTree, 0),
				conns:    make([]*clientConn, 0),
			}
			ut.Children = append(ut.Children, lastNode)

			lastNode.conns = append(lastNode.conns, conn)
			return
		}

		// 找到那个节点的孩子节点，进行更新client_conn
		isHave := false
		for k, v := range child.conns {
			if v.Cid == conn.Cid {
				// todo: 这个里面取消订阅应该把这个节点去掉
				// goto-retain: 新老连接掉线数据迁移
				if conn.OnceRetain == 0 {
					l := child.conns[k].RetainQueue
					for e := l.Front(); e != nil; e = e.Next() {
						conn.PushRetain(e.Value.(packets.ControlPacket))
					}
					conn.OnceRetain++
				}
				child.conns[k] = conn
				DEBUG_TREE.Println("更新连接")

				isHave = true
			}
		}
		if !isHave {
			child.conns = append(child.conns, conn)
		}

		return
	}
}

// Find2: 查询当前节点的孩子节点存不存在
// 主要是仅仅查询连接时候用
func (ut *UnorderedTree) Find2(name string) ([]*UnorderedTree, bool) {
	res := make([]*UnorderedTree, 0)
	DEBUG_TREE.Println(ut.Name, "孩子个数", len(ut.Children))
	for _, v := range ut.Children {
		if v.Name == name || v.Name == "+" || v.Name == "#" {
			res = append(res, v)
		}
	}

	if len(res) == 0 {
		return res, false
	}

	return res, true
}

// TreeData 拿出连接节点的数据结构
// 树的数据只能用这个指针取出来，因为是递归操作
type TreeData struct {
	Res []*UnorderedTree
}

func NewTreeData() *TreeData {
	return &TreeData{Res: make([]*UnorderedTree, 0)}
}

// Scan2 查询topic下面的所有tcp连接
func Scan2(topic string, ut *UnorderedTree, res *TreeData) {
	split := strings.SplitN(topic, "/", 2)
	if len(split) == 1 {
		ch, ok := ut.Find2(split[0])
		if !ok {
			DEBUG_TREE.Println("剩最后一嘚瑟退出")
			return
		}

		DEBUG_TREE.Println("找到节点", len(ch))
		res.Res = append(res.Res, ch...)

		return
	}

	if len(split) == 2 {
		ch, ok := ut.Find2(split[0])
		if !ok {
			DEBUG_TREE.Println(ut.Name, "查找不到节点s0退出", "s0", split[0], " s1: ", split[1])
			return
		}

		DEBUG_TREE.Println("需要递归的个数: ", len(ch))
		for _, v := range ch {
			DEBUG_TREE.Println("正在递归: ", v.Name)
			// 碰到这个全部截胡的，需要直接安排
			if v.Name == "#" {
				res.Res = append(res.Res, v)
			}
			Scan2(split[1], v, res)
		}
	}

	return
}

// RemoveNode 删除指定topic下面的节点
// - 取消订阅用的
// - 文件下面的也要删除
func RemoveNode(topic string, ut *UnorderedTree, conn *clientConn) {
	split := strings.SplitN(topic, "/", 2)
	if len(split) == 1 {
		isHave := false
		ch := &UnorderedTree{}
		for _, v := range ut.Children {
			if v.Name == split[0] {
				isHave = true
				ch = v
			}
		}
		if !isHave {
			return
		}
		res := make([]*clientConn, 0)
		for _, v := range ch.conns {
			if v.Cid != conn.Cid {
				res = append(res, v)
			}
		}
		ch.conns = res
		return
	}

	if len(split) == 2 {
		isHave := false
		ch := &UnorderedTree{}
		for _, v := range ut.Children {
			if v.Name == split[0] {
				ch = v
				isHave = true
			}
		}

		if isHave {
			RemoveNode(split[1], ch, conn)
		} else {
			return
		}
	}
}

// 作为调试，之打印第一条数据
func printTree(tree *UnorderedTree) {
	if tree != nil {
		fmt.Println(tree.Name)
		if len(tree.Children) != 0 {
			tree = tree.Children[0]
		}
	}
}
